///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef __BUOLA_APP_CEVENTLOOP_H__
#define __BUOLA_APP_CEVENTLOOP_H__

#include <buola/buola.h>

#include <map>
#include <buola/datetime/clocks.h>

namespace buola {
    
//TODO: modify things so that the fd poller doesn't spin constantly, and is only stopped when an actual event happens
//this would require adding some fd that can be used to wake up the poll call when some event is triggered from another
//thread, such as eventfd
    
///\addtogroup application
///@{

/// loop function hook

class CLoopFunction
{
public:
    virtual ~CLoopFunction()	{}
    //!returns true if we should continue processing events for this iteration
    virtual bool OnLoopFunction() { return true;    }
};

/////////////////////////////////////////////////////////////////////////////

/// event loop

/// Objects of this class represent an event loop. The CApp class contains
/// one such object, called the main loop.
///
/////////////////////////////////////////////////////////////////////////////

class CEventLoop : public UUncopiable
{
public:
    //! some standard priority values
    enum EPriority
    {
        PRIORITY_HIGHEST=0,
        PRIORITY_HIGH=100,
        PRIORITY_NORMAL=200,
        PRIORITY_MESSAGEQUEUE=250,
        PRIORITY_LOW=300,
        PRIORITY_LOWEST=400,
        PRIORITY_FDPOLLER=490,
        PRIORITY_IDLE=500
    };

//! \name constructor and destructor
//@{
    //! constructor

    //! \param pTime the time to sleep at the end of each iteration, if no
    //! input is available
    CEventLoop(const chrono::nanoseconds &pTime=chrono::milliseconds(20),bool pMainLoop=false);

    //! destructor
    virtual ~CEventLoop();
//@}

public:
//! \name adding hooks
//@{
    //! adds a function to be called in each iteration
    void AddFunction(int pPriority,CLoopFunction*,bool pWaits=false);
    //! removes a previously added function
    void RemoveFunction(CLoopFunction*);

    //! registers a timer
    void RegisterTimer(CTimer *pTimer);
    //! unregisters a timer
    void UnregisterTimer(CTimer *pTimer);
    //! updates a timer previously registered with this event loop
    void UpdateTimer(CTimer *pTimer);
//@}

//! \name time and synchronize
//@{
    //! sets the time to sleep at the end of each iteration
    void SetLoopTime(const chrono::nanoseconds &pTime);
    //! continues running this event loop in a new thread
    void ContinueInThread();
    //! cancels the thread that has been running this event loop and resumes normal execution
    void ResumeOutsideThread();
//@}

//! \name loop
//@{
    //! starts executing the loop
    void Loop();
    //! loops until pCondition becomes true
    void LoopUntil(bool &pCondition);

    //! asynchronously ends the loop

    //! the loop will finish at the end of the current iteration
    void End();

    //! returns true if the thread has been asynchronously ended
    bool GetExit()			{	return mExit;	}
//@}

//! \name mutex
//@{
    //! returns the synchronization mutex for the event loop
    recursive_mutex &GetMutex()		{	return mMutex;	}
//@}

private:
//! \name internal loop management
//@{
    void ThreadedLoop();
    void Iteration();

    //! returns the time until any timer will be activated
    chrono::nanoseconds CalcTimeout();
    //! checks all the timers and dispatches signals if any expired
    void CheckTimers();
//@}

private:
//! \name locking
//@{
    //! only the thread which has it can run the loop
    recursive_mutex mMutex;
//@}

//! \name hooks
//@{
    std::multimap<int,CLoopFunction*> mFunctions;
//@}

//! \name termination flags
//@{
    bool mExit;
    bool mExitThreadedLoop;
//@}

//! \name attributes
//@{
    //! time to wait during the polling stage
    chrono::nanoseconds mLoopTime;
    //! at least one of the functions performs a wait
    bool mHaveWaitingFunction;
    //! true if this is the main buola application loop
    bool mIsMainLoop;
//@}

//! \name timers
//@{
    //! time at which any of the timers will expire
    CMonotonicTime mNextTimer;
    //! keeps track of the timers associated to this event loop
    std::vector<CTimer*> mTimers;
//@}

    friend class io::CFDPoller;
};

///returns a reference to the main event loop
CEventLoop &get_main_event_loop();

///@}

/*namespace buola*/ }

#endif
